package com.framework.plm.bas.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class UtilKey {
	private static final Log log = LogFactory.getLog(UtilKey.class);
	
	public static class LongKeyGen {
		
		private static long IPAddressLongValue;

		static {
			try { 
				InetAddress addr = InetAddress.getLocalHost();
				byte[] ipaddr = addr.getAddress();
				//防止取出的地址在移位后发生符号为负的情况
				IPAddressLongValue = ((ipaddr[2] << 8) & 0xFF00) | (ipaddr[3] & 0xFF);
			} catch (UnknownHostException ex) { 
				log.error("Unknown Host Exception Caught: " + ex.getMessage());
			}
		}
	
		public synchronized long getLongKey() {
			StringBuffer strRetVal = new StringBuffer(String.valueOf(IPAddressLongValue));
			StringBuffer strTemp = new StringBuffer();
			long lastTime = System.currentTimeMillis();
			boolean done = false;
			while (!done) {
				long now = System.currentTimeMillis();
				if (now == lastTime) {
					// 如果服务器性能优越，则容易产生在很小的粒度时间内出现多次运算，也即这些运算能获取相同的系统时间，因此在此需要人工停顿进行干预
					try {
						Thread.sleep(1);
					} catch (java.lang.InterruptedException e) {
					} // ignore exception
					continue;
				} else {
					lastTime = now;
					done = true;
				}
			}
			strTemp = new StringBuffer(Long.toHexString(lastTime));
			while (strTemp.length() < 12) {
				strTemp.insert(0, '0');
			}
			//字符型的pk，在jvm集群级别可以满足需要
			strRetVal.append(strTemp);
			//long型的pk，在jvm集群级别可以满足需要
			long keyLong = (lastTime << 16) + IPAddressLongValue;
			return keyLong;
		}
	}
	
	public static class UUIDKeyGen {
		private static SecureRandom seederStatic;
		private static byte addr[];
		private static String midValueStatic = null;
		private String midValue;
		private SecureRandom seeder;
		
		public UUIDKeyGen(){
			midValue = null;
			seeder = null;
			StringBuffer buffer = new StringBuffer(16);
			buffer.append(midValueStatic);
			buffer.append(toHex(System.identityHashCode(this), 8));
			midValue = buffer.toString();
			seeder = new SecureRandom();
			seeder.nextInt();
    	}

    	public static String generate(Object obj){
        	StringBuffer uid = new StringBuffer(32);
        	long currentTimeMillis = System.currentTimeMillis();
        	uid.append(toHex((int)(currentTimeMillis & -1L), 8));
        	uid.append(midValueStatic);
        	uid.append(toHex(System.identityHashCode(obj), 8));
        	uid.append(toHex(getRandom(), 8));
        	return uid.toString();
    	}

    	public String generate(){
    		StringBuffer uid = new StringBuffer(32);
        	long currentTimeMillis = System.currentTimeMillis();
        	uid.append(toHex((int)(currentTimeMillis & -1L), 8));
        	uid.append(midValue);
        	uid.append(toHex(seeder.nextInt(), 8));
        	return uid.toString();
    	}

    	private static String toHex(int value, int length){
        	char hexDigits[] = {
            	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
            	'A', 'B', 'C', 'D', 'E', 'F'
        	};
        	StringBuffer buffer = new StringBuffer(length);
        	int shift = length - 1 << 2;
        	for(int i = -1; ++i < length;){
            	buffer.append(hexDigits[value >> shift & 0xf]);
            	value <<= 4;
        	}
        	return buffer.toString();
    	}

    	private static int toInt(byte bytes[]){
        	int value = 0;
        	for(int i = -1; ++i < bytes.length;){
            	value <<= 8;
            	value |= bytes[i];
        	}
        	return value;
    	}

    	private static synchronized int getRandom(){
        	return seederStatic.nextInt();
    	}

    	static{
        	seederStatic = null;
        	addr = null;
        	try{
            	addr = InetAddress.getLocalHost().getAddress();
            	StringBuffer buffer = new StringBuffer(8);
            	buffer.append(toHex(toInt(addr), 8));
            	midValueStatic = buffer.toString();
            	seederStatic = new SecureRandom();
            	seederStatic.nextInt();
        	}
        	catch(Exception ex) { }
    	}
	}
}
